import { windowIdOfTask } from "../config/index.js"
import { apiClient1 } from "../request/index.js"
import { freeWindowsData, closeWindowData } from "../api/adsApi/index.js"
import pLimit from "p-limit"
import { taskScheduler } from "./taskScheduler.js"

export { taskScheduler }

const WindowStatusesLimit = pLimit(2) // 每次最多 5 个并发请求
/**
 * 终止任务回调
 * @param {string} taskId - 任务 ID
 * @param {object} responseTopic - 自定义参数
 * @param {function} onAllBrowsersClosed - 当所有浏览器关闭后的回调函数
 * @param {Array<string>} [windowIdList] - 要关闭的特定窗口 ID 列表（可选）
 */
export async function stopTask(taskId, responseTopic, onAllBrowsersClosed, windowIdList = []) {
  console.log("taskId===>", taskId)

  const allWindows = windowIdOfTask[taskId] || []

  if (!allWindows.length) {
    console.log(`任务 ${taskId} 没有正在运行的窗口`)
    await onAllBrowsersClosed?.(responseTopic)
    return
  }

  // 根据是否传入 windowIdList 进行筛选
  const targetWindows = windowIdList.length ? allWindows.filter((id) => windowIdList.includes(id)) : allWindows

  if (!targetWindows.length) {
    console.log(`任务 ${taskId} 没有匹配的窗口需要关闭`)
    await onAllBrowsersClosed?.(responseTopic)
    return
  }

  console.log("准备关闭的窗口列表:", targetWindows)

  const closeBrowserPromises = targetWindows.map(async (windowId) => {
    try {
      const closeBrowserResponse = await closeWindowData({ windowId })
      console.log("closeBrowserResponse==>", closeBrowserResponse)
      return {
        windowId,
        success: closeBrowserResponse?.code !== -1,
      }
    } catch (error) {
      console.error("Error closing browser:", error)
      return { windowId, success: false }
    }
  })

  try {
    const closeResults = await Promise.all(closeBrowserPromises)

    const failed = closeResults.filter((res) => !res.success)
    const succeeded = closeResults.filter((res) => res.success)

    // 从原任务窗口列表中移除成功关闭的窗口
    windowIdOfTask[taskId] = allWindows.filter((id) => !succeeded.map((r) => r.windowId).includes(id))

    if (!failed.length) {
      console.log(`任务 ${taskId} 指定的窗口均已成功关闭`)
      await onAllBrowsersClosed?.(responseTopic)
    } else {
      console.log(
        `任务 ${taskId} 的部分窗口未能成功关闭:`,
        failed.map((f) => f.windowId)
      )
    }
  } catch (error) {
    console.error("Error closing browsers:", error)
  }
}

// 设置一个 Promise 来等待接口响应
const fetchResponseUser = (page, url, username, params) => {
  return new Promise(async (resolve, reject) => {
    page.on("response", async (response) => {
      // 检查响应的 URL 和状态码
      if (response.url().includes(url)) {
        try {
          const responseBody = await response.text()
          // 获取我们自己要的数据
          const { data } = JSON.parse(responseBody)
          const { user } = data
          if (user) {
            if (user) {
              // 获取关注
              params.fans[0].followersNum = user?.follower_count
              // 获取粉丝
              params.fans[0].followingNum = user?.following_count
              // 获取简介文案
              params.fans[0].selfIntroduction = user?.biography
            }
            resolve(params) // 返回数据
          }
        } catch (err) {
          reject(err)
        }
      }
    })
    await page.goto(`https://www.instagram.com/${username}`, { timeout: 60 * 1000 })
  })
}
/**
 * 列表滚动方法
 */
async function scrollToBottom(page, querySelectorStr) {
  const scrollContainer_tk = await page.$(querySelectorStr)
  console.log("scrollContainer_tk===>", scrollContainer_tk)
  if (scrollContainer_tk) {
    try {
      await page.evaluate(async (querySelectorStr) => {
        const container = document.querySelector(querySelectorStr)
        if (!container) return

        const scrollStep = 50 // 每次滚动的步长
        const timeout = 100 // 每次滚动后的延时

        const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

        // 滚动直到容器的 scrollTop 达到 scrollHeight
        while (container.scrollTop + container.clientHeight < container.scrollHeight) {
          container.scrollTop += scrollStep
          // 等待一段时间以模拟自然滚动
          await delay(timeout)
        }
      }, querySelectorStr)
    } catch (error) {
      console.log(error)
    }
  } else {
    console.log("未找到滚动容器")
  }
}
export async function safeEvaluate(page, selector, evalFunc) {
  try {
    const result = await page.$eval(selector, evalFunc)
    return result
  } catch (error) {
    // 元素没有找到，返回默认值
    console.log(`未找到元素: ${selector}, 返回默认值`)
    return null // 或者返回你希望的默认值
  }
}
/**
 * @param {string} value
 * @returns {number}
 */
export function convertExposure(value) {
  if (!value) return 0

  const lowerValue = value.trim().toLowerCase()

  if (lowerValue.endsWith("k")) {
    // Remove "k" and multiply by 1,000
    return parseFloat(lowerValue.slice(0, -1)) * 1_000
  } else if (lowerValue.endsWith("m")) {
    // Remove "m" and multiply by 1,000,000
    return parseFloat(lowerValue.slice(0, -1)) * 1_000_000
  } else if (lowerValue.endsWith("b")) {
    // Remove "b" and multiply by 1,000,000,000
    return parseFloat(lowerValue.slice(0, -1)) * 1_000_000_000
  } else {
    // Directly parse numeric values
    return parseFloat(lowerValue) || 0
  }
}
/**
 * 生成指定范围内的随机整数（包括最小值和最大值）
 * @param {number} min - 最小值（包括）
 * @param {number} max - 最大值（包括）
 * @returns {number} 生成的随机整数
 */
export function getRandomInteger(min, max) {
  // 将 min 和 max 转换为数字并检查是否有效
  const minNum = Number(min)
  const maxNum = Number(max)

  // 检查是否为有效数字
  if (isNaN(minNum) || isNaN(maxNum)) {
    console.log("min 或 max 不是有效的数字")
    return 0
  }

  // 确保 min <= max
  if (minNum > maxNum) {
    console.log("min 不能大于 max")
    return 0
  }

  // 生成随机整数
  return Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum
}
/**
 * 下载cdn资源
 */
export async function downloadCdnResource(videoUrl, userAgent, referer, browser) {
  const pageDownLoad = await browser.newPage()
  let videoPage = pageDownLoad // 记录最终加载视频的页面

  try {
    await pageDownLoad.setRequestInterception(true)
    pageDownLoad.on("request", (request) => {
      const headers = request.headers()
      headers["Referer"] = referer
      headers["User-Agent"] = userAgent
      request.continue({ headers })
    })

    let videoResponse
    pageDownLoad.on("response", async (response) => {
      if (response.url() === videoUrl) {
        videoResponse = response
      }
    })

    // 监听是否有新页面被打开
    browser.on("targetcreated", async (target) => {
      if (target.type() === "page") {
        const newPage = await target.page()
        if (newPage) {
          console.log("检测到新页面打开，更新 page 对象")
          videoPage = newPage // 更新最终页面
        }
      }
    })

    await pageDownLoad.goto(videoUrl, { waitUntil: "domcontentloaded" })

    // 等待可能的新页面打开
    await new Promise((resolve) => setTimeout(resolve, 3000))

    if (!videoResponse) {
      throw new Error("未找到视频响应")
    }

    const buffer = await videoResponse.buffer()
    console.log("cdn资源数据为：", buffer)
    console.log("cdn资源下载完成！")

    return buffer
  } finally {
    console.log("关闭页面")
    // 关闭最终的页面
    if (!videoPage.isClosed()) {
      console.log("尝试关闭新打开的页面")
      await videoPage.close({ runBeforeUnload: false })
    }
  }
}

/**
 * @param {string[]} exposures
 * @returns {number}
 */
export function sumExposures(exposures) {
  return exposures.map(convertExposure).reduce((total, current) => total + current, 0)
}

// 滚动页面并收集数据
export async function scrollAndCollectData(page) {
  let exposureDoms = []
  try {
    await page.evaluate(async () => {
      const distance = 100
      const delay = 100
      while (document.body.scrollHeight > window.scrollY + window.innerHeight) {
        window.scrollBy(0, distance)
        await new Promise((resolve) => setTimeout(resolve, delay))
      }
    })
  } catch (error) {
    console.log(error)
  }

  const exposureTempDoms = await page.$$('strong[data-e2e="video-views"]')
  for (const exposureTempDom of exposureTempDoms) {
    let text = await exposureTempDom.evaluate((el) => el.textContent)
    exposureDoms.push(text)
  }
  const exposureNum = sumExposures(exposureDoms)

  return {
    exposureNum,
    postTotal: exposureTempDoms.length,
  }
}
/**
 * 判断帖子的时间线是否符合要求
 * @param {string} postTime 帖子的时间 (支持 MM/DD, YYYY/MM/DD, now, 2d, 2m, 2h, 1w 等)
 * @param {string} timeFrame 时间范围 0-全部帖子,1-近三天,2-近一周,3-近两周
 * @returns {boolean}
 */
export function isPostTimeFrame(postTime, timeFrame) {
  const now = new Date()
  const currentYear = now.getFullYear()
  let postDate
  // **格式化日期为 YYYY-MM-DD**
  const formatDate = (date) => {
    return date.toISOString().split("T")[0]
  }
  // **标准化日期格式**
  const normalizeDate = (dateStr) => {
    const parts = dateStr.split("-")
    // 如果是 `M-DD` 或 `YYYY-M-DD` 格式，补全月份和日期
    if (parts.length === 2) {
      return `${currentYear}-${parts[0].padStart(2, "0")}-${parts[1].padStart(2, "0")}`
    } else if (parts.length === 3) {
      // 补全月份和日期
      return `${parts[0]}-${parts[1].padStart(2, "0")}-${parts[2].padStart(2, "0")}`
    }
    return dateStr
  }

  // **绝对时间格式**
  if (/^\d{2}\/\d{2}$/.test(postTime)) {
    postDate = new Date(`${currentYear}/${postTime}`)
  } else if (/^\d{4}\/\d{2}\/\d{2}$/.test(postTime)) {
    postDate = new Date(postTime)
  } else if (/^\d{2}-\d{2}$/.test(postTime)) {
    // MM-DD 格式，使用当前年份
    postDate = new Date(`${currentYear}-${postTime}`)
  } else if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(postTime)) {
    // YYYY-M-DD 格式，处理月份和日期补零
    postDate = new Date(normalizeDate(postTime))
  } else if (/^\d{4}-\d{2}-\d{2}$/.test(postTime)) {
    // YYYY-MM-DD 格式
    postDate = new Date(postTime)
  }
  // **相对时间格式**
  else {
    const relativeTimeMap = {
      now: 0,
      m: "minute",
      h: "hour",
      d: "day",
      w: "week",
      M: "month",
      分钟: "minute",
      小时: "hour",
      天: "day",
      周: "week",
      月: "month",
      分鐘: "minute",
      小時: "hour",
      週: "week",
      一週: "week",
      兩週: "2 weeks",
      日: "day", // 增加对 "日" 的支持
    }

    postDate = new Date(now) // 先设置为当前时间

    // 处理 `now`
    if (postTime === "now" || postTime === "现在" || postTime === "現 在" || postTime === "現在") {
      return new Date().toISOString().split("T")[0]
    }

    // 提取时间单位
    const match = postTime.match(/^(\d+)\s*([mhdwM分钟小时天周月分鐘小時週一週兩週]+)$/)
    if (match) {
      const value = parseInt(match[1], 10)
      const unit = match[2]
      const timeUnit = relativeTimeMap[unit]

      switch (timeUnit) {
        case "minute":
          postDate.setMinutes(now.getMinutes() - value)
          break
        case "hour":
          postDate.setHours(now.getHours() - value)
          break
        case "day":
          postDate.setDate(now.getDate() - value)
          break
        case "week":
          postDate.setDate(now.getDate() - value * 7)
          break
        case "month":
          postDate.setMonth(now.getMonth() - value)
          break
        default:
          throw new Error("Invalid relative time format.")
      }
    } else {
      throw new Error("Invalid postTime format. Expected MM/DD, YYYY/MM/DD, or relative time (now, 2d, 2m, 2h, 1w, etc.)")
    }
  }

  if (isNaN(postDate.getTime())) {
    throw new Error("Invalid date.")
  }

  // 计算时间范围
  const timeFrames = {
    1: 4, // 近三天（包含第3天）
    2: 8, // 近一周（包含第7天）
    3: 15, // 近两周（包含第14天）
  }
  const daysLimit = timeFrames[timeFrame]
  const diffTime = now - postDate
  const diffDays = diffTime / (1000 * 60 * 60 * 24)
  if (timeFrame == 0) {
    console.log("要处理全部帖子")
    return formatDate(postDate)
  }
  return diffDays <= daysLimit ? formatDate(postDate) : false
}
/**
 * 定时查找页面中的某个元素
 * @param {object} page - Puppeteer 页面对象
 * @param {string} selector - 要查找的 CSS 选择器
 * @param {number} interval - 轮询间隔（毫秒）
 * @param {number} timeout - 最大等待时间（毫秒）
 * @returns {Promise<ElementHandle|null>} 找到元素则返回 ElementHandle，未找到返回 null
 */
export async function waitForSelectorWithInterval(page, selector, interval = 60000, timeout = 10 * 60 * 1000) {
  const startTime = Date.now()

  while (Date.now() - startTime < timeout) {
    try {
      const element = await page.$(selector)
      if (element) {
        console.log(`元素已找到：${selector}`)
        return element
      } else {
        console.log(`未找到元素，${interval / 1000}s 后重试...`)
        await new Promise((res) => setTimeout(res, interval))
      }
    } catch (err) {
      console.error(`查找元素出错: ${err.message}`)
      await new Promise((res) => setTimeout(res, interval))
    }
  }

  console.warn(`超时未找到元素：${selector}`)
  return null
}
export async function getWindowStatuses(config, mode, adsConfig) {
  const results = []

  for (let i = 0; i < config.maxWindows; i++) {
    const userId = adsConfig[`userId_${mode}${i + 1}`]
    if (!userId) continue

    const result = await WindowStatusesLimit(async () => {
      const freeResult = await freeWindowsData(userId)
      console.log("freeResult===>", freeResult)
      return {
        windowIndex: i,
        isFree: freeResult?.data.status === "Inactive",
      }
    })

    results.push(result)

    // 限流：每次请求后等待 500ms
    await new Promise((resolve) => setTimeout(resolve, 1000))
  }

  return results
}

export function sendAutomationMessage(type, payload) {
  const message = { source: "automation-script", type, payload }
  console.log(JSON.stringify(message))
}
/**
 * 传入数据配置综合，单个所取字段别名，返回配置项值。输入属性名，直接返回属性值
 * @param {Array} arr 配置数组
 * @param {string} proxyName 所取字段别名
 * @param {string} name 属性名
 */
export function arrayToValueOfConfig(arr, proxyName, name = "") {
  try {
    for (let index = 0; index < arr.length; index++) {
      const item = arr[index]
      if (name) {
        return item[name]
      }
      if (item.name == proxyName) {
        return item.value
      }
    }
  } catch (error) {
    console.log("arrayToValueOfConfig-error", error)
  }
}
/**
 * 双向map
 */
export function createBiMap() {
  const forwardMap = new Map()
  const reverseMap = new Map()

  function set(key, value) {
    forwardMap.set(key, value)
    reverseMap.set(value, key)
  }

  function get(keyOrValue, isKey = true) {
    return isKey ? forwardMap.get(keyOrValue) : reverseMap.get(keyOrValue)
  }

  function deleteEntry(keyOrValue, isKey) {
    const oppositeMap = isKey ? reverseMap : forwardMap
    const originalMap = isKey ? forwardMap : reverseMap

    const oppositeKeyOrValue = originalMap.get(keyOrValue)
    oppositeMap.delete(oppositeKeyOrValue)
    originalMap.delete(keyOrValue)
  }

  function hasKey(key) {
    return forwardMap.has(key)
  }

  function hasValue(value) {
    return reverseMap.has(value)
  }

  function getForwardMap() {
    return forwardMap
  }

  return {
    set,
    get,
    delete: deleteEntry,
    hasKey,
    hasValue,
    getForwardMap,
  }
}
/**
 * 把{app_name}等替换成相应的值，然后返回
 *
 * @param {*} inputString
 * @param {*} map
 * @returns
 */
export function replacePlaceholder(inputString, map, task) {
  console.log("task?.host?.name====>", task?.host?.name)
  const newString = inputString.replace(/\{(.*?)\}/g, (_, key) => `{${task?.host?.name}__${key}}`)
  // 使用正则表达式匹配字符串中的占位符，例如{app_name}
  const regex = /\{([^}]+)\}/g

  // 使用 replace 方法进行替换
  const replacedString = newString.replace(regex, (match, placeholder) => {
    // 从映射中查找相应的值
    console.log("{${placeholder}}===>", `{${placeholder}}`)
    const replacement = map.get(`{${placeholder}}`, true)
    // 如果找到替换值，则返回替换值，否则保持原样
    return replacement !== undefined ? replacement : match
  })

  return replacedString
}
/**
 * 把值转成{app_name}
 *
 * @param {*} inputString
 * @param {*} biMap
 * @returns
 */
export function replaceStringWithPlaceholder(inputString, biMap) {
  // 遍历映射表，将映射表中的值替换为对应的占位符
  biMap.getForwardMap().forEach((value, key) => {
    const regex = new RegExp(escapeRegExp(value), "g")
    inputString = inputString.replace(regex, key)
  })

  return inputString
}

// 函数用于对字符串中的特殊字符进行转义，以确保正则表达式的正确匹配
export function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
}
/**
 * 关闭所有不是目标 URL 的标签页
 * @param {puppeteer.Browser} browser - 已连接的 Puppeteer browser 实例
 * @param {string[]} targetUrls - 要保留的目标页面 URL 数组（支持部分匹配）
 */
export async function closeUnwantedPages(browser, targetUrls) {
  const pages = await browser.pages()

  for (const page of pages) {
    const url = page.url()

    const isTarget = targetUrls.some((target) => url.includes(target))

    if (!isTarget) {
      try {
        await page.close()
        console.log(`✅ 关闭非目标页面: ${url}`)
      } catch (err) {
        console.warn(`❌ 无法关闭页面 ${url}:`, err)
      }
    } else {
      console.log(`🔒 保留目标页面: ${url}`)
    }
  }
}

// API请求限流控制
export const apiRateLimiter = {
  lastRequestTime: 0,
  minInterval: 1000, // 最小间隔1秒
  queue: [],
  isProcessing: false,

  async request(fn) {
    return new Promise((resolve, reject) => {
      // 将请求加入队列
      this.queue.push({ fn, resolve, reject })

      // 如果没有正在处理的请求，开始处理队列
      if (!this.isProcessing) {
        this.processQueue()
      }
    })
  },

  async processQueue() {
    if (this.queue.length === 0) {
      this.isProcessing = false
      return
    }

    this.isProcessing = true
    const now = Date.now()
    const timeToWait = Math.max(0, this.lastRequestTime + this.minInterval - now)

    if (timeToWait > 0) {
      await new Promise((resolve) => setTimeout(resolve, timeToWait))
    }

    const { fn, resolve, reject } = this.queue.shift()
    this.lastRequestTime = Date.now()

    try {
      const result = await fn()
      resolve(result)
    } catch (error) {
      reject(error)
    }

    // 处理下一个请求
    this.processQueue()
  },
}
/**
 * 传入字符串。兼容各种常用单位描述转数据
 * @param {*} text
 * @returns
 */
export function smartParseNumber(text) {
  if (typeof text !== "string") return 0
  text = text.trim()

  // 繁体转简体
  text = text.replace(/萬/g, "万").replace(/億/g, "亿").replace(/成員/g, "成员").replace(/兩/g, "二") // '兩' 转 '二'

  const cnNums = { 零: 0, 一: 1, 二: 2, 三: 3, 四: 4, 五: 5, 六: 6, 七: 7, 八: 8, 九: 9 }
  const cnUnits = { 十: 10, 百: 100, 千: 1000, 万: 10000, 亿: 100000000 }

  function chineseToNumber(cn) {
    let result = 0
    let section = 0
    let number = 0
    let unit = 1

    for (let i = 0; i < cn.length; i++) {
      const char = cn[i]
      if (cnNums[char] != null) {
        number = cnNums[char]
        if (i === cn.length - 1) {
          section += number
        }
      } else if (cnUnits[char]) {
        unit = cnUnits[char]
        if (unit === 10000 || unit === 100000000) {
          section = (section + (number || 0)) * unit
          result += section
          section = 0
        } else {
          section += (number || 1) * unit
        }
        number = 0
      }
    }
    return result + section
  }

  // 特别处理如 1百万，2千万，3亿万
  const specialUnitMap = {
    百万: 1000000,
    千万: 10000000,
    亿万: 10000000000,
    十万: 100000,
    百亿: 10000000000,
  }

  for (const [key, multiplier] of Object.entries(specialUnitMap)) {
    const reg = new RegExp("(\\d+(\\.\\d+)?)" + key)
    const match = text.match(reg)
    if (match) {
      return Math.round(parseFloat(match[1]) * multiplier)
    }
  }

  // 数字+单位 (比如 1.2万 1.2m 2k)
  const numWithUnitReg = /([\d,.]+)\s*([万亿wmbkＫＭＢ]?)/i
  const numMatch = text.match(numWithUnitReg)
  if (numMatch) {
    let [, numStr, unit] = numMatch
    numStr = numStr.replace(/,/g, "")
    let number = parseFloat(numStr)
    if (isNaN(number)) return 0

    unit = unit.toLowerCase()
    if (unit === "w" || unit === "万") number *= 10000
    else if (unit === "m") number *= 1000000
    else if (unit === "b") number *= 1000000000
    else if (unit === "k" || unit === "Ｋ") number *= 1000
    else if (unit === "亿") number *= 100000000

    return Math.round(number)
  }

  // 纯中文数字
  const chineseFullNumReg = /[零一二三四五六七八九十百千万亿]+/
  const chineseMatch = text.match(chineseFullNumReg)
  if (chineseMatch) {
    return chineseToNumber(chineseMatch[0])
  }

  return 0
}
export function generatePositiveInt64() {
  // 生成高位和低位的随机数
  var high = Math.floor(Math.random() * 0x100000000)
  var low = Math.floor(Math.random() * 0x100000000)

  // 将高位和低位进行位运算，构造成一个 Int64 类型的随机数
  var int64 = (high << 32) | low

  // 返回生成的 Int64 随机数的绝对值
  return Math.abs(int64)
}

